﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace QUT.Bio.Util {

	/// <summary>
	/// ProcessWrapper extends Process class to simplify input and output redirection.
	/// </summary>
	public class ProcessWrapper : System.Diagnostics.Process {

		private TextReader standardInput = null;
		private StringWriter stdout = new StringWriter();

		/// <summary>
		/// The contents of the standatrd output stream.
		/// </summary>
		public string Stdout {
			get {
				return stdout.ToString();
			}
		}

		private StringWriter stderr = new StringWriter();

		/// <summary>
		/// The contents of the standard error stream.
		/// </summary>
		public string Stderr {
			get {
				return stderr.ToString();
			}
		}

		/// <summary>
		/// Initialise this GPFlowProcess object.
		/// </summary>
		/// <param name="program">Name of the program to execute. May be fully qualified, or appear in the path.</param>
		/// <param name="args">A string containing the argument list for the child process.</param>
		/// <param name="workDirectory">The location in which the child process should start.</param>
		/// <param name="standardInput">Redirected standard input stream contents, if any. If none, use 'null'.</param>
		public ProcessWrapper (
			string program,
			string args,
			string workDirectory,
			string standardInput
		) {
			StartInfo.FileName = program;
			StartInfo.Arguments = args;
			StartInfo.WorkingDirectory = workDirectory;
			StartInfo.UseShellExecute = false;

			StartInfo.RedirectStandardInput = ( standardInput != null );
			StartInfo.RedirectStandardOutput = true;
			StartInfo.RedirectStandardError = true;

			StartInfo.CreateNoWindow = true;
			StartInfo.ErrorDialog = false;
			StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

			OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler( stdoutHandler );
			ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler( stderrHandler );

			if ( standardInput != null ) {
				this.standardInput = new StringReader( standardInput );
			}
		}

		/// <summary>
		/// Initialise this GPFlowProcess object.
		/// </summary>
		/// <param name="program">Name of the program to execute. May be fully qualified, or appear in the path.</param>
		/// <param name="workDirectory">The location in which the child process should start.</param>
		/// <param name="standardInput">Redirected standard input stream contents, if any. If none, use 'null'.</param>
		/// <param name="args">An string containing the argument list for the child process.</param>
		public ProcessWrapper (
			FileInfo program,
			DirectoryInfo workDirectory,
			TextReader standardInput,
			params string[] args
		) {
			StartInfo.FileName = program.FullName;
			StartInfo.Arguments = string.Join( " ", args );
			StartInfo.WorkingDirectory = workDirectory.FullName;
			StartInfo.UseShellExecute = false;

			StartInfo.RedirectStandardInput = ( standardInput != null );
			StartInfo.RedirectStandardOutput = true;
			StartInfo.RedirectStandardError = true;

			OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler( stdoutHandler );
			ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler( stderrHandler );

			this.standardInput = standardInput;
		}

		/// <summary>
		/// Initialise this GPFlowProcess object.
		/// </summary>
		/// <param name="program">Name of the program to execute. May be fully qualified, or appear in the path.</param>
		/// <param name="workDirectory">The location in which the child process should start.</param>
		/// <param name="standardInput">Redirected standard input stream contents, if any. If none, use 'null'.</param>
		/// <param name="args">An string containing the argument list for the child process.</param>
		public ProcessWrapper (
			FileInfo program,
			DirectoryInfo workDirectory,
			TextReader standardInput,
			params object[] args
		)
			: this( program, workDirectory, standardInput, Convert( args ) ) {
		}

		private static string[] Convert ( object[] x ) {
			return Array.ConvertAll<object, string>( x, new Converter<object, string>( delegate( object t ) {
				return t.ToString();
			} ) );
		}

		/// <summary>
		/// Starts the child process and begins asynchronous reads on standard input and standard error
		/// to capture output.
		/// </summary>
		public new void Start () {
			if ( base.Start() ) {
				BeginOutputReadLine();
				BeginErrorReadLine();

				if ( base.StartInfo.RedirectStandardInput ) {
					string s;
					while ( ( s = standardInput.ReadLine() ) != null ) {
						StandardInput.WriteLine( s );
					}
				}
			}
		}

		/// <summary>
		/// Asynchronous output handler, captures stdout from external programs.
		/// </summary>
		/// <param name="sendingProcess">The process that is sending data.</param>
		/// <param name="outLine">A line of data from the sender.</param>

		private void stdoutHandler (
			object sendingProcess,
			System.Diagnostics.DataReceivedEventArgs outLine
		) {
			if ( !String.IsNullOrEmpty( outLine.Data ) ) {
				stdout.WriteLine( outLine.Data );
			}
		}

		/// <summary>
		/// Asynchronous output handler, captures stdout from external programs.
		/// </summary>
		/// <param name="sendingProcess">The process that is sending data.</param>
		/// <param name="outLine">A line of data from the sender.</param>
		private void stderrHandler (
			object sendingProcess,
			System.Diagnostics.DataReceivedEventArgs outLine
		) {
			if ( !String.IsNullOrEmpty( outLine.Data ) ) {
				stderr.WriteLine( outLine.Data );
			}
		}
	}
}
